/* 
ncname: 匹配标签名 形如 abc-123
qnameCapture: 匹配特殊标签 形如 abc:234 前面的abc:可有可无
startTagOpen: 匹配标签开始 形如 <abc-123 捕获里面的标签名
startTagClose: 匹配标签结束  >
endTag: 匹配标签结尾 如 </abc-123> 捕获里面的标签名
attribute: 匹配属性  形如 id="app" 
defaultTagRE: 匹配双花括号
*/
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
const startTagOpen = new RegExp(`^<${qnameCapture}`);
const startTagClose = /^\s*(\/?)>/; 
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`);
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; 
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;

// 解析 HTML , 生成 ast 语法树
function parseHTML (html) {
	// 根据传入长度，截取 HTML
	function advance (n) {  // advance adj 预先的，n 进展
		html = html.substring(n);
	}

	let root;
	let stack = []; // 标签集合栈 【<div><span><i></i></span></div>】
	function createAstElement (tagName, attrs) {
		return {
			tag: tagName,
			attrs,
			children: [],
			parent: null,
			type: 1
		}
	}



	function start (tagName, attrs) {
		let element = createAstElement(tagName, attrs);
		if (root == null) {
			root = element;
		}
		let parent = stack[stack.length - 1];
		if (parent) {
			element.parent = parent;
			parent.children.push(element);
		}
		stack.push(element);
	}

	function end (tagName) {
		stack.pop(); // 从数组中删除最后一个元素
	}

	function chars (text) {
		text = text.replace(/\s/g, ''); // 将文本中的空格替换为空
		if (text) {
			let parent = stack[stack.length - 1];
			parent.children.push({
				type: 3,
				text
			})
		}
	}

	while (html) {
		let textEnd = html.indexOf('<');
		if (textEnd === 0) {
			const startTagMatch = parseStartTag();
			// startTagMatch 就是 {tagName: 'div', attrs: Array(2)}
			if (startTagMatch) {
				start(startTagMatch.tagName, startTagMatch.attrs);
				continue
			}

			// 结束标签
			let endTagMatch;
			if (endTagMatch = html.match(endTag)) {
				end(endTagMatch[1]);
				advance(endTagMatch[0].length)
				continue
			}
		}
		let text;
		if (textEnd >= 0) {
			text = html.substring(0, textEnd);
		}
		if (text) {
			advance(text.length);
			chars(text);
			// break
		}
	}
	// 解析开始标签
	function parseStartTag () {
		const matches = html.match(startTagOpen);
		if (matches) {
			const match = {
				tagName: matches[1],
				attrs: []
			}
			advance(matches[0].length); // 到此截取完了开始标签 <div
			let end, attr;
			while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
				match.attrs.push({
					name: attr[1],
					value: attr[3] || attr[4] || attr[5] || true
				});
				advance(attr[0].length);
				// 到此截取完属性 id="app"
			}
			// 删除结束尖角号 >
			if (end) {
				advance(end[0].length);
				return match;
			}
		}
	}
	return root;
}

// createElement 变成 _c 
// createTextVnode 变成 _v
// 双花括号的文本 变成 _s(xxx)
// 处理属性
function genProps (attrs) {
	let str = '';
	for (let i = 0; i < attrs.length; i++) {
		let attr = attrs[i];
		if (attr.name === 'style') {
			let obj = {};
			attr.value.split(';').reduce((memo, current) => {
				let [key, value] = current.split(':');
				memo[key] = value;
				return memo;
			}, obj);
			attr.value = obj;
		}
		str += `${attr.name}: ${JSON.stringify(attr.value)},`;
	}
	return `{${str.slice(0, -1)}}`
}

function gen (node) {
	if (node.type == 1) {
		return genCode(node);
	} else {
		let text = node.text;
		if (!defaultTagRE.test(text)) {
			return `_v(${JSON.stringify(text)})`
		} else {
			let tokens = [];
			let match;
			let startIndex = defaultTagRE.lastIndex = 0;
			while (match = defaultTagRE.exec(text)) {
				let endIndex = match.index;
				if (endIndex > startIndex) {
					tokens.push(JSON.stringify(text.slice(startIndex, endIndex)));
				}
				tokens.push(`_s(${match[1].trim()})`);
				startIndex = endIndex + match[0].length;
			}
			if (startIndex < text.length) {
				tokens.push(JSON.stringify(text.slice(startIndex)));
			}
			return `_v(${tokens.join('+')})`
		}
	}
}

// 生成子标签
function genChildren (ast) {
	const children = ast.children;
	return children.map(child => gen(child)).join(',');
}

// 将 ast 语法树转换成 render 函数
function genCode (ast) {
	let code;
	code = `_c('${ast.tag}', ${ast.attrs.length > 0 ? genProps(ast.attrs) : 'undefined'}${
		ast.children ? ',' + genChildren(ast) : ''
	})`
	return code;
}

export function compileToFunctions (template) {
	let ast = parseHTML(template);
	let code = genCode(ast);
	const render = new Function(`with (this) {
		return ${code}
	}`);
	return render;
}